﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Pyco.Framework;

using System.Net;
using System.IO;
using HtmlAgilityPack;
using System.Threading;
using System.Xml;
using System.Net.Sockets;
using Excel;
using ModelText.ModelEditControl;
using System.Configuration;
using ModelText.ModelDom.Nodes;
using ModelText.ModelDom.Range;
using ModelText.ModelEditToolCommands;


namespace AutoSendMail
{
    public partial class FormSendMail : Form
    {
        MailProvider mailProvider = new MailProvider();
        bool completed = false;

        TooledControl m_modelTextHtmlControl;
        Settings m_settings;
        const string filenameOfReadme = "../../../readme.html";
        ToolTip m_toolTip;
        string m_toolTipText;

        public FormSendMail()
        {
            InitializeComponent();
            initialize();
        }

        private void initialize()
        {
            //log any unhandled exceptions
            ModelText.ModelException.LogUnhandledException.enable(true);

            //get this program's persisted settings
            m_settings = new Settings();

            //instantiate a ModelText HTML control
            m_modelTextHtmlControl = new TooledControl();

            //create the control's toolbar
            m_modelTextHtmlControl.addTools();

            //add the control to the form
            //by superimposing it on the panel1 element
            //which I added to the form using the Visual Studio Designer to act as a
            //placeholder for the location of the ModelText HTML control within the form.
            m_modelTextHtmlControl.Dock = DockStyle.Fill;
            panel1.Controls.Add(m_modelTextHtmlControl);

            //load the data after the form as finished loading
            this.Load += new EventHandler(Form1_Load);

            //install an event handler, to help process some of the buttons on the edit toolbar
            modelEdit.toolContainer.onToolCommand = onToolCommand;


            //instantiate (but don't show) the tooltip instance
            this.components = new System.ComponentModel.Container();
            m_toolTip = new ToolTip(this.components);

        }

        /// <summary>
        /// This method is invoked when any command on the toolbar is pressed
        /// </summary>
        /// <param name="command"></param>
        void onToolCommand(Command command)
        {
            //see what type of command it is
            //the command-handler for most of the commands are implemented within the control
            //but two of the commands (i.e. Save and InsertHyperlink) need help from the client application
            if (object.ReferenceEquals(command, CommandInstance.commandInsertHyperlink))
            {
                //invoke a method to handle the Insert Hyperlink command being pressed
                onInsertHyperlink();
            }
            else if (object.ReferenceEquals(command, CommandInstance.commandSave))
            {
                //invoke the same method as the one that's invoked via the form's File/Save menu item
                saveOrSaveAs();
            }
        }

        bool saveOrSaveAs()
        {
            if (m_settings.recentFilename != null)
            {
                return onSave(m_settings.recentFilename);
            }
            else
            {
                return onSaveAs();
            }
        }

        bool onSave(string filename)
        {
            try
            {
                string tempFilename = Path.GetTempFileName();
                using (StreamWriter textWriter = new StreamWriter(tempFilename))
                {
                    modelEdit.save(textWriter, XmlHeaderType.Xhtml);
                }
                File.Copy(tempFilename, filename, true);
                setCurrentFilename(filename);
                return true;
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
                return false;
            }
        }

        bool onSaveAs()
        {
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "XHTML Files|*.html";
            saveFileDialog1.Title = "Save XHTML File";
            if (saveFileDialog1.ShowDialog() != DialogResult.OK)
            {
                //user cancelled
                return false;
            }

            //get the filename from the dialog box
            string filename = saveFileDialog1.FileName;
            //try to save the file from the control
            return onSave(filename);
        }
        private void btnSend_Click(object sender, EventArgs e)
        {
            String filePath = txtFile.Text;
            if (!File.Exists(filePath))
            {
                MessageBox.Show("Đường dẫn File không hợp lệ");
                return;
            }

            FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read);

            //1. Reading from a binary Excel file ('97-2003 format; *.xls)
            IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
            //...
            //2. Reading from a OpenXml Excel file (2007 format; *.xlsx)
            //IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
    
            excelReader.IsFirstRowAsColumnNames = false ;
            DataSet result = excelReader.AsDataSet();

            excelReader.Close();

            btnSend.Enabled = false;

            progressBar1.Value = 0;
            progressBar1.Step = 1;
            mailProvider.MailSent_Completed += new MailProvider.SentMailHandler(MailSent_Complete);
            mailProvider.MailSentAll_Completed += new MailProvider.SentMailHandler(MailSentAll_Complete);

            MailList mailList = new MailList();
            for (int i = 0; i < result.Tables[0].Rows.Count; i++)
            {
                mailList.addToMailList(result.Tables[0].Rows[i][0].ToString());
            }
            progressBar1.Maximum = mailList.Count();
            XmlDocument doc = new XmlDocument();
            String path = Application.StartupPath + "\\SMTPConfig.xml";
            if (!File.Exists(path))
            {
                MessageBox.Show("Không thể Load File SMTPConfig.xml");
                return;
            }
            doc.Load(path);
            for (int i = 0; i < doc.ChildNodes[1].ChildNodes.Count; i++)
            {
                String host = doc.ChildNodes[1].ChildNodes[0].ChildNodes[0].InnerText;
                int port = Convert.ToInt32(doc.ChildNodes[1].ChildNodes[0].ChildNodes[1].InnerText);
                String user = doc.ChildNodes[1].ChildNodes[0].ChildNodes[2].InnerText;
                String pass = doc.ChildNodes[1].ChildNodes[0].ChildNodes[3].InnerText;
                SMTPConfig smtpConfig = new SMTPConfig(host, port, user, pass);
                smtpConfig.MaxUse = AppConfig.MaxUseForOneSMTP;
                smtpConfig.CurrentUse = 0;
                mailProvider.addToSmtpList(smtpConfig);
            }

            mailProvider.SmtpList = mailList;
            mailProvider.SenderEmail = txtSenderEmail.Text;
            mailProvider.SenderName = txtSenderName.Text;
            mailProvider.Subject = txtSubject.Text;
            mailProvider.Body = GetContent();
            mailProvider.CheckEmailAddress = chkCheckEmailAddress.Checked;



           // mailProvider.Body = modelEdit.domDocument;
            
            Thread oThread = new Thread(new ThreadStart(start));
            oThread.Start();
        }

        private String GetContent()
        {
            String filename = Application.StartupPath + AppConfig.TempHtmlFilePath + AppConfig.TempHtmlFileName;
            string tempFilename = Path.GetTempFileName();
            using (StreamWriter textWriter = new StreamWriter(tempFilename))
            {
                modelEdit.save(textWriter, XmlHeaderType.Xhtml);
            }
            File.Copy(tempFilename, filename, true);

            StreamReader reader = File.OpenText(filename);
            return reader.ReadToEnd();
        }

        private void start()
        {
            mailProvider.sendAll();
        }

        private void MailSent_Complete(object sender)
        {

        }
        private void MailSentAll_Complete(object sender)
        {
            completed = true;
            MessageBox.Show("Complete Sending mail");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            InitData();
        }

        private void InitData()
        {
            chkCheckEmailAddress.Checked = AppConfig.CheckEmailAddress;
            txtSenderEmail.Text = AppConfig.SenderEmail;
            txtSenderName.Text = AppConfig.SenderName;
            txtSubject.Text = AppConfig.MailSubject;

            String filename = Application.StartupPath + AppConfig.TempHtmlFilePath + AppConfig.TempHtmlFileName;
            if (File.Exists(filename))
            {
                //try to load the file into the control
                if (load(filename, true))
                    return;

                //failed to load the specified file
                DialogResult dialogResult = MessageBox.Show(
                    "Failed to load the specified file; maybe it isn't HTML. Try again with another file?",
                    "Cannot Open This File",
                    MessageBoxButtons.OKCancel);

                if (dialogResult != DialogResult.OK)
                    return;
            }

            if (!Directory.Exists(Application.StartupPath + AppConfig.TempHtmlFilePath))
            {
                Directory.CreateDirectory(Application.StartupPath + AppConfig.TempHtmlFilePath);
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (progressBar1.Value < progressBar1.Maximum)
                progressBar1.Value = mailProvider.NumberOfEmailHasBeenAccessed;
            if (completed == true)
                btnSend.Enabled = true;
        }

        private void btnFile_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Excel Worksheets|*.xls";
            openFileDialog1.Title = "Chọn File";
            openFileDialog1.Multiselect = false;
            openFileDialog1.ShowDialog();

            txtFile.Text = openFileDialog1.FileName;
        }

        private void btnHtml_Click(object sender, EventArgs e)
        {
            //repeat indefinitely until success or until the user quits
            for (; ; )
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog();
                openFileDialog1.Filter = "XHTML Files|*.html";
                openFileDialog1.Title = "Select a XHTML File";
                if (openFileDialog1.ShowDialog() != DialogResult.OK)
                {
                    //user cancelled
                    return;
                }

                //get the filename from the dialog box
                string filename = openFileDialog1.FileName;
                //try to load the file into the control
                if (load(filename, true))
                    return;

                //failed to load the specified file
                DialogResult dialogResult = MessageBox.Show(
                    "Failed to load the specified file; maybe it isn't HTML. Try again with another file?",
                    "Cannot Open This File",
                    MessageBoxButtons.OKCancel);

                if (dialogResult != DialogResult.OK)
                    return;

                //else loop to try again
            }
        }

        /// <summary>
        /// This method attempts to load the contents of the specified file into the control.
        /// </summary>
        bool load(string filename, bool canEditThisFile)
        {
            if (filename == null)
                return false;
            try
            {
                using (StreamReader textReader = new StreamReader(filename))
                {
                    modelEdit.openDocument(textReader);
                }

                //successfully loaded this file
                afterNewDocumentLoaded((canEditThisFile) ? filename : null);

                return true;
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
                return false;
            }
        }

        /// <summary>
        /// Gets the IModelEdit interface which is implemented by the ModelText HTML Control.
        /// </summary>
        IModelEdit modelEdit
        {
            get { return m_modelTextHtmlControl.modelEdit; }
        }

        void setCurrentFilename(string filename)
        {
            //reopen this file if the program is next restarted
            m_settings.recentFilename = filename;
        }

        /// <summary>
        /// This method is called from the onNew and load methods.
        /// </summary>
        void afterNewDocumentLoaded(string filename)
        {
            //enable menu items
          
            //remember the current filename
            setCurrentFilename(filename);
            //hide any tooltip possibly left over fom a previous document
            hyperlinkHref = null;

            //install event handlers to display anchors' href values in a tooltip on hover
            ModelText.ModelDom.Events.DomEventListeners.onAnchor(
                modelEdit.domDocument,
                ModelText.ModelDom.Events.DomEventType.Mousemove,
                delegate(string hrefValue) { this.hyperlinkHref = hrefValue; });
            ModelText.ModelDom.Events.DomEventListeners.onAnchor(
                modelEdit.domDocument,
                ModelText.ModelDom.Events.DomEventType.Mouseout,
                delegate(string hrefValue) { this.hyperlinkHref = null; });

            //install an event handler, which can do something when the user clicks on an anchor
            modelEdit.domDocument.addDefaultEventListener(ModelText.ModelDom.Events.DomEventType.Click, onClick);
        }

        void onInsertHyperlink()
        {
            //get the document fragment which the user has currently selected using mouse and/or cursor
            IWindowSelection windowSelection = modelEdit.windowSelection;
            //verify that there's only one selection
            if (windowSelection.rangeCount != 1)
            {
                //this can happen when the user has selected several cell in a table,
                //in which case each cell is a separate selection/range
                MessageBox.Show("Can't insert a hyperlink when more than one range in the document is selected");
                return;
            }
            using (IDomRange domRange = windowSelection.getRangeAt(0))
            {
                //verify that only one node is selected
                if (!domRange.startContainer.isSameNode(domRange.endContainer))
                {
                    //this can happen for example when the selection spans multiple paragraphs
                    MessageBox.Show("Can't insert a hyperlink when more than one node in the document is selected");
                    return;
                }
                IDomNode container = domRange.startContainer; //already just checked that this is the same as domRange.endContainer
                //read existing values from the current selection
                string url;
                string visibleText;
                IDomElement existingHyperlink;
                switch (container.nodeType)
                {
                    case DomNodeType.Text:
                        //selection is a text fragment
                        visibleText = container.nodeValue.Substring(domRange.startOffset, domRange.endOffset - domRange.startOffset);
                        IDomNode parentNode = container.parentNode;
                        if ((parentNode.nodeType == DomNodeType.Element) && (parentNode.nodeName == "a"))
                        {
                            //parent of this text node is a <a> element
                            existingHyperlink = parentNode as IDomElement;
                            url = existingHyperlink.getAttribute("href");
                            visibleText = container.nodeValue;
                            if ((existingHyperlink.childNodes.count != 1) || (existingHyperlink.childNodes.itemAt(0).nodeType != DomNodeType.Text))
                            {
                                //this can happen when an anchor tag wraps more than just a single, simple text node
                                //for example when it contains inline elements like <strong>
                                MessageBox.Show("Can't edit a complex hyperlink");
                                return;
                            }
                        }
                        else
                        {
                            existingHyperlink = null;
                            url = null;
                        }
                        break;

                    default:
                        //unexpected
                        MessageBox.Show("Can't insert a hyperlink when more than one node in the document is selected");
                        return;
                }
                //display the modal dialog box
                using (FormInsertHyperlink formInsertHyperlink = new FormInsertHyperlink())
                {
                    formInsertHyperlink.url = url;
                    formInsertHyperlink.visibleText = visibleText;
                    DialogResult dialogResult = formInsertHyperlink.ShowDialog();
                    if (dialogResult != DialogResult.OK)
                    {
                        //user cancelled
                        return;
                    }
                    //get new values from the dialog box
                    //the FormInsertHyperlink.onEditTextChanged method assures that both strings are non-empty
                    url = formInsertHyperlink.url;
                    visibleText = formInsertHyperlink.visibleText;
                }
                //need to change href, change text, and possibly delete existing text;
                //do this within the scope of a single IEditorTransaction instance so
                //that if the user does 'undo' then it will undo all these operations at once, instead of one at a time
                using (IEditorTransaction editorTransaction = modelEdit.createEditorTransaction())
                {
                    if (existingHyperlink != null)
                    {
                        //changing an existing hyperlink ...
                        //... change the href attribute value
                        existingHyperlink.setAttribute("href", url);
                        //... change the text, by removing the old text node and inserting a new text node
                        IDomText newDomText = modelEdit.domDocument.createTextNode(visibleText);
                        IDomNode oldDomText = existingHyperlink.childNodes.itemAt(0);
                        existingHyperlink.removeChild(oldDomText);
                        existingHyperlink.insertBefore(newDomText, null);
                    }
                    else
                    {
                        //creating a new hyperlink
                        IDomElement newHyperlink = modelEdit.domDocument.createElement("a");
                        IDomText newDomText = modelEdit.domDocument.createTextNode(visibleText);
                        newHyperlink.insertBefore(newDomText, null);
                        newHyperlink.setAttribute("href", url);
                        //remove whatever was previously selected, if anything
                        if (!domRange.collapsed)
                        {
                            domRange.deleteContents();
                        }
                        //insert the new hyperlink
                        domRange.insertNode(newHyperlink);
                    }
                }
            }
        }

        void onClick(ModelText.ModelDom.Events.IDomEvent domEvent)
        {
            //see whether the clicked node is an anchor element
            //(or an ancestor of the clicked node, because the clicked node
            //is actually the text node which is a child of the anchor node)
            for (IDomNode target = domEvent.target; target != null; target = target.parentNode)
            {
                if ((target.nodeType == DomNodeType.Element) && (target.nodeName == "a"))
                {
                    //invoke the method which would be invoked if the user
                    //pressed the "Hyperlink" button on the toolbar
                    onInsertHyperlink();
                    return;
                }
            }
        }

        string hyperlinkHref
        {
            set
            {
                if (value == null)
                {
                    //disable the tooltip
                    m_toolTip.Hide(m_modelTextHtmlControl);
                    m_toolTipText = null;
                }
                else
                {
                    //compare with cached m_toolTipText to avoid flickering
                    if (value == m_toolTipText)
                    {
                        //this tooltip value is already displayed
                        return;
                    }
                    m_toolTipText = value;
                    //show the tooltip, wherever the mouse currently is
                    Point point = Cursor.Position; //screen coordinates
                    point = m_modelTextHtmlControl.PointToClient(point); //client coordinates
                    point.Offset(0, -50); //higher, to avoid obscuring the text
                    m_toolTip.Show(value, m_modelTextHtmlControl, point);
                }
            }
        }
    }

    public class Settings : ApplicationSettingsBase
    {
        internal Settings()
        {
            base.Reload();
        }

        [UserScopedSetting()]
        [DefaultSettingValue(null)]
        public string recentFilename
        {
            get { return (string)this["recentFilename"]; }
            set { this["recentFilename"] = value; base.Save(); }
        }
    }
}
